package com.carter.springframework.beans.factory.config;

import com.carter.springframework.beans.exception.BeanException;
import com.carter.springframework.beans.factory.DisposableBean;
import com.carter.springframework.beans.factory.ObjectFactory;

import java.util.HashMap;
import java.util.Map;

public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
    protected static final Object NULL_OBJECT = new Object();
    //一级缓存，存放已经处理完成的bean
    private final Map<String, Object> singletonBeans = new HashMap<>();
    //二级缓存，存放半成品bean
    protected final Map<String, Object> earlySingletonObjects = new HashMap<>();
    //三级缓存，存放代理对象
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();

    private final Map<String, DisposableBean> disposableBeans = new HashMap<>();

    @Override
    public Object getSingleton(String beanName) {
        //先从一级缓存中取
        Object bean = singletonBeans.get(beanName);
        if (bean == null) {
            //从二级缓存中取
            bean = earlySingletonObjects.get(beanName);
            if (bean == null) {
                //从三级缓存中取
                ObjectFactory<?> factory = singletonFactories.get(beanName);
                if (factory != null) {
                    bean = factory.getObject();
                    //将bean从三级缓存中移入二级缓存(移入的是getObject之后的结果),同时移除三级缓存中的数据
                    earlySingletonObjects.put(beanName,bean);
                    singletonFactories.remove(beanName);
                }
            }
        }
        return bean;
    }

    @Override
    public void addSingleton(String beanName, Object bean) {
        //在添加单例bean的时候，放入一级缓存，同时要注意清理二级缓存和三级缓存
        singletonBeans.put(beanName, bean);
        earlySingletonObjects.remove(beanName);
        singletonFactories.remove(beanName);
    }

    /**
     * 添加一个bean进入三级缓存
     * 这里在添加进入之后，要移除二级缓存中的数据
     * 因为可能对于这个bean，生成了多个三级缓存
     * 而在前面的某个三级缓存已经移入二级缓存
     * 所以需要移除二级缓存，以最新的三级缓存为主
     * @param beanName
     * @param objectFactory
     */
    protected void addSingletonFactory(String beanName,ObjectFactory<?> objectFactory){
        if (!singletonFactories.containsKey(beanName)){
            singletonFactories.put(beanName,objectFactory);
            earlySingletonObjects.remove(beanName);
        }
    }

    @Override
    public void registerDisposableBean(String beanName, DisposableBean bean) {
        disposableBeans.put(beanName, bean);
    }

    public void destroySingletons() {
        String[] keys = disposableBeans.keySet().toArray(new String[0]);
        for (String key : keys) {
            DisposableBean bean = disposableBeans.remove(key);
            try {
                bean.destroy();
            } catch (Exception e) {
                throw new BeanException("exception on destroy bean " + key, e);
            }
        }
    }
}
